import pLimit from "p-limit";
import type {IPart} from "./types.ts";
import minioApi from '@/api/modules/minio';
import type {IRawFile} from "../types.ts";
import type {UploadProgressEvent} from "element-plus";

export class UploadTask {
  /**
   * 最大并发数
   */
  private readonly MAX_CONCURRENT = 5;

  /**
   * 文件分片映射
   * @private
   */
  private fileMap: Record<string, IPart[]> = {};

  /**
   * @private
   */
  private _limit = pLimit(this.MAX_CONCURRENT);

  uploadFile(option: {
    file: IRawFile,
    uploadId: string,
    uploadUrls: string[],
    partSize: number,
    partCount: number,
    signal?: AbortSignal,
    onProgress?: (event: UploadProgressEvent) => void,
  }) {
    const allParts = new Array(option.partCount).fill(0).map((_, index) => index + 1);

    const parts = option.uploadUrls.map((url) => this.createPart(url, option));

    if (parts.length !== allParts.length) {
      // 这里是在进行部分上传，补充上传，更新进度条
      parts.forEach(part => {
        let indexOf = allParts.indexOf(part.partNumber);
        if (indexOf !== -1) {
          allParts.splice(indexOf, 1);
        }
      })
      parts.push(...allParts.map(partNumber => ({
        uploadId: option.uploadId,
        partNumber,
        partSize: option.partSize,
        loaded: option.partSize,
        status: 'success',
        uploadPartTask: () => Promise.resolve(),
      } as IPart)));
      // 更新进度条
      calculatePercentage(parts, option.file.size);
    }

    this.fileMap[option.uploadId] = parts;

    return Promise.all(parts.map(part => this._limit(() => part.uploadPartTask())));
  }

  /**
   * 创建分片对象
   * @param url - 分片上传的 URL
   * @param option - 上传选项
   * @returns 分片对象
   */
  private createPart(url: string, option: {
    file: IRawFile,
    uploadId: string,
    uploadUrls: string[],
    partSize: number,
    signal?: AbortSignal,
    onProgress?: (event: UploadProgressEvent) => void,
  }): IPart {
    const searchParams = new URL(url).searchParams;
    const partNumber = parseInt(searchParams.get('partNumber') as string, 10);

    // 计算上传进度
    const calculatePercentage = (uploadId: string, total: number) => {
      const parts = this.fileMap[uploadId];
      const loaded = parts.reduce((prev, part) => prev + part.loaded, 0);
      return Math.min(100, Math.round((loaded / total) * 100));
    }

    return {
      uploadId: option.uploadId,
      partNumber,
      partSize: option.partSize,
      loaded: 0,
      status: 'ready',
      async uploadPartTask() {
        if (option.signal?.aborted) {
          this.status = 'cancel';
          return Promise.resolve();
        }

        if (this.status !== 'ready') return Promise.resolve();

        this.status = 'uploading';

        try {
          const {file, partSize, signal, onProgress} = option;
          const start = (partNumber - 1) * partSize;
          const end = Math.min(partNumber * partSize, file.size);
          const chunk = file.slice(start, end);

          await minioApi.uploadPart(url, chunk, {
            signal,
            onUploadProgress: (event: UploadProgressEvent) => {
              this.loaded = event.loaded;

              const percentage = calculatePercentage(option.uploadId, file.size);
              onProgress?.({
                loaded: event.loaded,
                total: file.size,
                percent: percentage
              } as UploadProgressEvent);
            }
          });

          this.status = 'success';
        } catch (error) {
          console.error(`Error uploading part ${partNumber}:`, error);
          this.status = 'fail';
          throw error;
        }
      }
    } as IPart;
  }
}


/**
 * 计算上传进度
 * @param parts 所有片数
 * @param total 文件总大小
 */
function calculatePercentage(parts: IPart[], total: number) {
  const loaded = parts.reduce((prev, part) => prev + part.loaded, 0);
  return Math.min(100, Math.round((loaded / total) * 100));
}
